﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace DARE.Effects.RenderEffect
{
    public abstract class AEffectParam
    {
        #region Fields

        private AEffectParam m_parent;
        private List<AEffectParam> m_childrens;
        private Dictionary<Type, PropertyInfo[]> m_properties;

        private static List<Object> m_values;
        private static Dictionary<Object, int> m_valuesCounter;

        private Dictionary<string, int> m_params;

        #endregion

        #region Properties

        public AEffectParam Parent
        {
            get { return this.m_parent; }
            private set
            {
                if (this.m_parent != value)
                {
                    if (this.m_parent != null)
                        this.m_parent.m_childrens.Remove(this);
                    this.m_parent = value;
                    if (this.m_parent != null)
                        this.m_parent.m_childrens.Add(this);
                }
            }
        }

        public CEffect Effect { get; private set; }

        #endregion

        #region CTOR

        static AEffectParam()
        {
            m_values = new List<object>();
            m_values.Add(null);
            m_valuesCounter = new Dictionary<object, int>();
        }

        public AEffectParam(CEffect _effect) : this(_effect, null) { }

        public AEffectParam(CEffect _effect, AEffectParam _parent)
        {
            Parent = _parent;
            this.Effect = _effect;
            m_params = new Dictionary<string, int>();
            m_properties = new Dictionary<Type, PropertyInfo[]>();
            m_childrens = new List<AEffectParam>();
        }

        #endregion

        #region Method

        protected void SetData(string name, Object value)
        {
            if (!m_params.ContainsKey(name))
                m_params.Add(name, 0);
            else
            {
                var obj = m_values[m_params[name]];
                if (obj == value)
                    return;

                //if (obj != null)
                //{
                //    m_valuesCounter[obj]--;
                //    if (m_valuesCounter[obj] <= 0)
                //    {
                //        m_values.Remove(obj);
                //        m_valuesCounter.Remove(obj);
                //    }
                //}
            }
            bool found = false;
            for (int i = 0; i < m_values.Count; ++i)
            {
                if (m_values[i] == value || (value != null && value.Equals(m_values[i]) && m_values[i].GetType() == value.GetType()))
                {
                    m_params[name] = i;
                    //if (value != null)
                    //    m_valuesCounter[value]++;
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                m_params[name] = m_values.Count;
                m_values.Add(value);
                //m_valuesCounter.Add(value, 1);
            }
            foreach (var elem in m_childrens)
                elem.SetData(name, value);
        }

        protected T GetData<T>(string name)
        {
            if (m_params.ContainsKey(name))
                return (T)m_values[m_params[name]];
            throw new InvalidOperationException("Didn't contains the Data index with the name :" + name);
        }

        private PropertyInfo[] InitializePropertysInfo(Type _type)
        {
            return _type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty);
        }

        public virtual void FillEffect(CEffect effect)
        {
            //if (m_properties == null)
            //    m_properties = new Dictionary<Type, PropertyInfo[]>();
            //Type _type = effect.GetType();
            //if (!m_properties.ContainsKey(_type))
            //    m_properties.Add(_type, InitializePropertysInfo(_type));

            //var properties = m_properties[_type];
            //Parallel.ForEach(properties, (_prop) =>
            //{
            //    if (m_params.ContainsKey(_prop.Name))
            //    {
            //        var _value = m_values[m_params[_prop.Name]];
            //        if (_prop.GetValue(effect, null) != _value)
            //            _prop.SetValue(effect, _value, null);
            //    }
            //});


            effect.LighteningEnabled = (bool)m_values[m_params["LighteningEnabled"]];
            effect.EmissiveLightColor = (Color)m_values[m_params["EmissiveLightColor"]];
            effect.EmissiveLightIntensity = (float)m_values[m_params["EmissiveLightIntensity"]];
            effect.AmbientLightColor = (Color)m_values[m_params["AmbientLightColor"]];
            effect.AmbientLightIntensity = (float)m_values[m_params["AmbientLightIntensity"]];
            effect.DirectionalLight0Direction = (Vector3)m_values[m_params["DirectionalLight0Direction"]];
            effect.DirectionalLight0DiffuseColor = (Color)m_values[m_params["DirectionalLight0DiffuseColor"]];
            effect.DirectionalLight0DiffuseIntensity = (float)m_values[m_params["DirectionalLight0DiffuseIntensity"]];
            effect.DirectionalLight0SpecularColor = (Color)m_values[m_params["DirectionalLight0SpecularColor"]];
            effect.DirectionalLight0SpecularIntensity = (float)m_values[m_params["DirectionalLight0SpecularIntensity"]];
            effect.DirectionalLight1Direction = (Vector3)m_values[m_params["DirectionalLight1Direction"]];
            effect.DirectionalLight1DiffuseColor = (Color)m_values[m_params["DirectionalLight1DiffuseColor"]];
            effect.DirectionalLight1DiffuseIntensity = (float)m_values[m_params["DirectionalLight1DiffuseIntensity"]];
            effect.DirectionalLight1SpecularColor = (Color)m_values[m_params["DirectionalLight1SpecularColor"]];
            effect.DirectionalLight1SpecularIntensity = (float)m_values[m_params["DirectionalLight1SpecularIntensity"]];
            effect.DirectionalLight2Direction = (Vector3)m_values[m_params["DirectionalLight2Direction"]];
            effect.DirectionalLight2DiffuseColor = (Color)m_values[m_params["DirectionalLight2DiffuseColor"]];
            effect.DirectionalLight2DiffuseIntensity = (float)m_values[m_params["DirectionalLight2DiffuseIntensity"]];
            effect.DirectionalLight2SpecularColor = (Color)m_values[m_params["DirectionalLight2SpecularColor"]];
            effect.DirectionalLight2SpecularIntensity = (float)m_values[m_params["DirectionalLight2SpecularIntensity"]];
            effect.PerPixelLighteningEnabled = (bool)m_values[m_params["PerPixelLighteningEnabled"]];
            effect.UseSceneParameters = (bool)m_values[m_params["UseSceneParameters"]];
            effect.IsAnimationActive = (bool)m_values[m_params["IsAnimationActive"]];
            effect.IsDecalsActive = (bool)m_values[m_params["IsDecalsActive"]];
            effect.EyePosition = (Vector3)m_values[m_params["EyePosition"]];
            effect.IsMultiTexturingActive = (bool)m_values[m_params["IsMultiTexturingActive"]];
            if (m_params.ContainsKey("ColorMapTiles1"))
                effect.ColorMapTiles1 = (Vector2)m_values[m_params["ColorMapTiles1"]];
            if (m_params.ContainsKey("ColorMapOffset1"))
                effect.ColorMapOffset1 = (Vector2)m_values[m_params["ColorMapOffset1"]];
            if (m_params.ContainsKey("ColorMap1"))
                effect.ColorMap1 = (Texture2D)m_values[m_params["ColorMap1"]];
            if (m_params.ContainsKey("ColorMapTiles2"))
                effect.ColorMapTiles2 = (Vector2)m_values[m_params["ColorMapTiles2"]];
            if (m_params.ContainsKey("ColorMapOffset2"))
                effect.ColorMapOffset2 = (Vector2)m_values[m_params["ColorMapOffset2"]];
            if (m_params.ContainsKey("ColorMap2"))
                effect.ColorMap2 = (Texture2D)m_values[m_params["ColorMap2"]];
            if (m_params.ContainsKey("ColorMapTiles3"))
                effect.ColorMapTiles3 = (Vector2)m_values[m_params["ColorMapTiles3"]];
            if (m_params.ContainsKey("ColorMapOffset3"))
                effect.ColorMapOffset3 = (Vector2)m_values[m_params["ColorMapOffset3"]];
            if (m_params.ContainsKey("ColorMap3"))
                effect.ColorMap3 = (Texture2D)m_values[m_params["ColorMap3"]];
            if (m_params.ContainsKey("MultiTexturingMapTiles"))
                effect.MultiTexturingMapTiles = (Vector2)m_values[m_params["MultiTexturingMapTiles"]];
            if (m_params.ContainsKey("MultiTexturingMapOffset"))
                effect.MultiTexturingMapOffset = (Vector2)m_values[m_params["MultiTexturingMapOffset"]];
            if (m_params.ContainsKey("MultiTexturingMap"))
                effect.MultiTexturingMap = (Texture2D)m_values[m_params["MultiTexturingMap"]];
            if (m_params.ContainsKey("DecalsProjectorMatrix"))
                effect.DecalsProjectorMatrix = (Matrix)m_values[m_params["DecalsProjectorMatrix"]];
            if (m_params.ContainsKey("DecalsMap"))
                effect.DecalsMap = (Texture2D)m_values[m_params["DecalsMap"]];
            effect.Brighteness = (float)m_values[m_params["Brighteness"]];
            if (m_params.ContainsKey("Bones"))
                effect.Bones = (Matrix[])m_values[m_params["Bones"]];
            if (m_params.ContainsKey("World")) 
                effect.World = (Matrix)m_values[m_params["World"]];
            if (m_params.ContainsKey("View")) 
                effect.View = (Matrix)m_values[m_params["View"]];
            if (m_params.ContainsKey("Projection")) 
                effect.Projection = (Matrix)m_values[m_params["Projection"]];
            effect.ColorMapOffset = (Vector2)m_values[m_params["ColorMapOffset"]];
            effect.ColorMapTiles = (Vector2)m_values[m_params["ColorMapTiles"]];
            effect.ColorMap = (Texture2D)m_values[m_params["ColorMap"]];
            effect.Alpha = (float)m_values[m_params["Alpha"]];

        }

        #endregion

    }
}
